﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;

namespace Ingenious
{
    public class XsdHelper
    {
        #region Constants
        public const string XsdNamespace = "http://www.w3.org/2001/XMLSchema#";
        public const string XsdDefaultPrefix = "xsd";

        public const string XsdAnyUri = "anyURI";
        public const string XsdBase64Binary = "base64Binary";
        public const string XsdBoolean = "Boolean";
        public const string XsdByte = "Byte";
        public const string XsdDate = "Date";
        public const string XsdDateTime = "dateTime";
        public const string XsdDecimal = "decimal";
        public const string XsdDouble = "Double";
        public const string XsdDuration = "duration";
        public const string XsdEntities = "ENTITIES";
        public const string XsdEntity = "ENTITY";
        public const string XsdFloat = "float";
        public const string XsdGDay = "gDay";
        public const string XsdGMonthDay = "gMonthDay";
        public const string XsdGYear = "gYear";
        public const string XsdGYearMonth = "gYearMonth";
        public const string XsdHexBinary = "hexBinary";
        public const string XsdID = "ID";
        public const string XsdIDREF = "IDREF";
        public const string XsdIDREFS = "IDREFS";
        public const string XsdInt = "int";
        public const string XsdInteger = "integer";
        public const string XsdLanguage = "language";
        public const string XsdLong = "long";
        public const string XsdMonth = "month";
        public const string XsdName = "Name";
        public const string XsdNCName = "NCName";
        public const string XsdNegativeInteger = "negativeInteger";
        public const string XsdNMTOKEN = "NMTOKEN";
        public const string XsdNMTOKENS = "NMTOKENS";
        public const string XsdNonNegativeInteger = "nonNegativeInteger";
        public const string XsdNonPositiveInteger = "nonPositiveInteger";
        public const string XsdNormalizedString = "normalizedString";
        public const string XsdNOTATION = "NOTATION";
        public const string XsdPositiveInteger = "positiveInteger";
        public const string XsdQName = "QName";
        public const string XsdShort = "short";
        public const string XsdString = "string";
        public const string XsdTime = "time";
        public const string XsdTimePeriod = "timePeriod";
        public const string XsdToken = "token";
        public const string XsdUnsignedByte = "unsignedByte";
        public const string XsdUnsignedInt = "unsignedInt";
        public const string XsdUnsignedLong = "unsignedLong";
        public const string XsdUnsignedShort = "unsignedShort";
        #endregion

        private static Dictionary<string, Type> s_mapXsdTypeToType =
            new Dictionary<string, Type>();

        private static void AddXsdUriToTypeMap(string xsdUri, Type mapType)
        {
            // Make a case insensitive map
            s_mapXsdTypeToType.Add(xsdUri.ToUpper(), mapType);
        }

        // Note: See http://msdn.microsoft.com/en-us/library/aa719879(v=vs.71).aspx
        private static void InitializeXsdUriToTypeMap()
        {
            AddXsdUriToTypeMap(XsdAnyUri, typeof(Uri));
            AddXsdUriToTypeMap(XsdBase64Binary.ToUpper(), typeof(byte[]));
            AddXsdUriToTypeMap(XsdBoolean, typeof(bool));
            AddXsdUriToTypeMap(XsdByte, typeof(SByte));
            AddXsdUriToTypeMap(XsdDate, typeof(DateTime));
            AddXsdUriToTypeMap(XsdDateTime, typeof(DateTime));
            AddXsdUriToTypeMap(XsdDecimal, typeof(decimal));
            AddXsdUriToTypeMap(XsdDouble, typeof(double));
            AddXsdUriToTypeMap(XsdDuration, typeof(TimeSpan));
            AddXsdUriToTypeMap(XsdEntities, typeof(string[]));
            AddXsdUriToTypeMap(XsdEntity, typeof(string));
            AddXsdUriToTypeMap(XsdFloat, typeof(float));
            AddXsdUriToTypeMap(XsdGDay, typeof(DateTime));
            AddXsdUriToTypeMap(XsdGYearMonth, typeof(DateTime));
            AddXsdUriToTypeMap(XsdHexBinary, typeof(byte[]));
            AddXsdUriToTypeMap(XsdID, typeof(string));
            AddXsdUriToTypeMap(XsdIDREF, typeof(string));
            AddXsdUriToTypeMap(XsdIDREFS, typeof(string[]));
            AddXsdUriToTypeMap(XsdLanguage, typeof(string));
            AddXsdUriToTypeMap(XsdLong, typeof(Int64));
            AddXsdUriToTypeMap(XsdMonth, typeof(DateTime));
            AddXsdUriToTypeMap(XsdName, typeof(string));
            AddXsdUriToTypeMap(XsdNCName, typeof(string));
            AddXsdUriToTypeMap(XsdNegativeInteger, typeof(string));
            AddXsdUriToTypeMap(XsdNMTOKEN, typeof(string));
            AddXsdUriToTypeMap(XsdNMTOKENS, typeof(string[]));
            AddXsdUriToTypeMap(XsdNonNegativeInteger, typeof(decimal));
            AddXsdUriToTypeMap(XsdNonPositiveInteger, typeof(decimal));
            AddXsdUriToTypeMap(XsdQName, typeof(XmlQualifiedName));
            AddXsdUriToTypeMap(XsdShort, typeof(Int16));
            AddXsdUriToTypeMap(XsdString, typeof(string));
            AddXsdUriToTypeMap(XsdTime, typeof(DateTime));
            AddXsdUriToTypeMap(XsdTimePeriod, typeof(TimeSpan));
            AddXsdUriToTypeMap(XsdToken, typeof(string));
            AddXsdUriToTypeMap(XsdUnsignedByte, typeof(byte));
            AddXsdUriToTypeMap(XsdUnsignedInt, typeof(UInt32));
            AddXsdUriToTypeMap(XsdUnsignedLong, typeof(UInt64));
            AddXsdUriToTypeMap(XsdUnsignedShort, typeof(UInt16));
        }

        #region Object Lifetime
        static XsdHelper()
        {
            InitializeXsdUriToTypeMap();
        }
        #endregion

        public static bool TryGetTypeForSchemaType(string qualifiedType, out Type type)
        {
            type = null;
            string xsdType = null;

            if (qualifiedType.Contains(XsdNamespace))
            {
                if (qualifiedType.Length == XsdNamespace.Length)
                {
                    // Cannot just contain the XSD namespace
                    return false;
                }
                else
                {
                    xsdType = qualifiedType.Substring(XsdNamespace.Length, qualifiedType.Length - XsdNamespace.Length);
                }
            }
            else
            {
                xsdType = qualifiedType;
            }

            s_mapXsdTypeToType.TryGetValue(xsdType.ToUpper(), out type);            
            return type != null;
        }

        public bool TryParseValueAsSchemaType(out object value)
        {
            value = null;
            return false;
        }
    }
}

